package com.nbcio.modules.erp.doc.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nbcio.common.core.domain.R;
import com.nbcio.modules.erp.doc.domain.ErpBill;
import com.nbcio.modules.erp.doc.domain.ErpBillItem;
import com.nbcio.modules.erp.doc.service.IErpBillService;
import com.nbcio.modules.erp.doc.service.IErpBillItemService;
import com.nbcio.modules.erp.coderule.service.ICodeRuleService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@Tag(name = "单据管理")
@RestController
@RequestMapping("/erp/bill")
@RequiredArgsConstructor
public class ErpBillController {

    private final IErpBillService billService;
    private final IErpBillItemService itemService;
    private final ICodeRuleService codeRuleService;

    @GetMapping("/page")
    public R<IPage<ErpBill>> page(@RequestParam int pageNum,
                                  @RequestParam int pageSize,
                                  @RequestParam String billType,
                                  @RequestParam(required = false) String status,
                                  @RequestParam(required = false) String billNo,
                                  @RequestParam(required = false) String warehouseId,
                                  @RequestParam(required = false) String partyId,
                                  @RequestParam(required = false) String startTime,
                                  @RequestParam(required = false) String endTime) {
        LambdaQueryWrapper<ErpBill> qw = new LambdaQueryWrapper<>();
        qw.eq(ErpBill::getBillType, billType);
        if (status != null && !status.isEmpty()) qw.eq(ErpBill::getStatus, status);
        if (billNo != null && !billNo.isEmpty()) qw.like(ErpBill::getBillNo, billNo);
        if (warehouseId != null && !warehouseId.isEmpty()) qw.eq(ErpBill::getWarehouseId, Long.parseLong(warehouseId));
        if (partyId != null && !partyId.isEmpty()) qw.eq(ErpBill::getPartyId, Long.parseLong(partyId));
        if (startTime != null && endTime != null && !startTime.isEmpty() && !endTime.isEmpty()) qw.between(ErpBill::getSubmitTime, startTime, endTime);
        return R.ok(billService.page(Page.of(pageNum, pageSize), qw));
    }

    @GetMapping("/nextNo")
    public R<String> getNextBillNo(@RequestParam String billType) {
        String billNo = codeRuleService.nextBillNo(billType);
        return R.ok("操作成功", billNo);
    }

    @GetMapping("/{id:\\d+}")
    public R<ErpBill> get(@PathVariable("id") String id) {
        return R.ok(billService.getById(Long.parseLong(id)));
    }

    @PostMapping
    public R<Boolean> create(@RequestBody ErpBill bill) {
        bill.setStatus("DRAFT");
        if (bill.getBillNo() == null || bill.getBillNo().isEmpty()) {
            bill.setBillNo(codeRuleService.nextBillNo(bill.getBillType()));
        }
        return R.ok(billService.save(bill));
    }

    @PutMapping("/{id}")
    public R<Boolean> update(@PathVariable("id") String id, @RequestBody ErpBill bill) {
        bill.setBillId(Long.parseLong(id));
        return R.ok(billService.updateById(bill));
    }

    @PostMapping("/{id}/submit")
    public R<Boolean> submit(@PathVariable("id") String id) {
        return R.ok(billService.submit(id));
    }

    @PostMapping("/{id}/approve")
    public R<Boolean> approve(@PathVariable("id") String id) {
        return R.ok(billService.approve(id));
    }

    @PostMapping("/{id}/post")
    public R<Boolean> post(@PathVariable("id") String id) {
        return R.ok(billService.post(id));
    }

    @GetMapping("/{id}/proc")
    public R<String> getProcInsId(@PathVariable("id") String id,
                                   org.flowable.engine.RuntimeService runtimeService,
                                   org.flowable.engine.HistoryService historyService) {
        var runtime = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(id).singleResult();
        if (runtime != null) return R.ok(runtime.getId());
        var hist = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(id).singleResult();
        return R.ok(hist == null ? null : hist.getId());
    }

    // 明细增删改
    @PostMapping("/{id}/items")
    public R<Boolean> addItem(@PathVariable("id") String id, @RequestBody ErpBillItem item) {
        item.setBillId(Long.parseLong(id));
        return R.ok(itemService.save(item));
    }

    @GetMapping("/{id}/items")
    public R<java.util.List<ErpBillItem>> listItems(@PathVariable("id") String id) {
        java.util.List<ErpBillItem> items = itemService.lambdaQuery().eq(ErpBillItem::getBillId, Long.parseLong(id)).orderByAsc(ErpBillItem::getLineNo).list();
        return R.ok(items);
    }

    @PutMapping("/{id}/items/{itemId}")
    public R<Boolean> updateItem(@PathVariable("id") String id,
                                 @PathVariable("itemId") String itemId,
                                 @RequestBody ErpBillItem item) {
        item.setId(Long.parseLong(itemId));
        item.setBillId(Long.parseLong(id));
        return R.ok(itemService.updateById(item));
    }

    @DeleteMapping("/{id}/items/{itemId}")
    public R<Boolean> deleteItem(@PathVariable("id") String id,
                                 @PathVariable("itemId") String itemId) {
        return R.ok(itemService.removeById(Long.parseLong(itemId)));
    }

    // 保存/提交一体化
    public static class BillSaveSubmitDTO {
        public ErpBill bill;
        public java.util.List<ErpBillItem> items;
        public boolean submit;
    }

    @PostMapping("/save-submit")
    public R<Boolean> saveAndMaybeSubmit(@RequestBody BillSaveSubmitDTO dto) {
        ErpBill bill = dto.bill;
        
        // 校验必填字段
        if (bill.getWarehouseId() == null) {
            return R.fail("仓库不能为空");
        }
        
        // 校验单据类型
        if (bill.getBillType() == null || bill.getBillType().isEmpty()) {
            return R.fail("单据类型不能为空");
        }
        if (!"PO_IN".equals(bill.getBillType()) && !"SO_OUT".equals(bill.getBillType())) {
            return R.fail("无效的单据类型: " + bill.getBillType());
        }
        
        if (bill.getBillId() == null) {
            // 新建单据
            if (bill.getBillNo() == null || bill.getBillNo().isEmpty()) {
                // 如果没有单据号，自动生成
                bill.setBillNo(codeRuleService.nextBillNo(bill.getBillType()));
            }
            bill.setStatus("DRAFT");
            billService.save(bill);
        } else {
            // 更新单据：检查状态，已过账的单据不允许修改
            ErpBill existingBill = billService.getById(bill.getBillId());
            if (existingBill == null) {
                return R.fail("单据不存在");
            }
            if ("POSTED".equals(existingBill.getStatus())) {
                return R.fail("该单据已过账，无法编辑。如需修改，请先取消过账或创建新单据。");
            }
            
            // 保留原有的状态和时间字段，避免被覆盖
            bill.setStatus(existingBill.getStatus());
            bill.setSubmitTime(existingBill.getSubmitTime());
            bill.setApproveTime(existingBill.getApproveTime());
            
            billService.updateById(bill);
            // 先清空旧明细再写新明细（简化处理）
            itemService.lambdaUpdate().eq(ErpBillItem::getBillId, bill.getBillId()).remove();
        }
        if (dto.items != null) {
            for (int i = 0; i < dto.items.size(); i++) {
                ErpBillItem it = dto.items.get(i);
                it.setBillId(bill.getBillId());
                if (it.getLineNo() == null) it.setLineNo(i + 1);
                // 计算并设置行金额（如果未设置或为0）
                if (it.getAmount() == null || it.getAmount().compareTo(java.math.BigDecimal.ZERO) == 0) {
                    if (it.getUnitPriceExcl() != null && it.getQty() != null) {
                        it.setAmount(it.getUnitPriceExcl().multiply(it.getQty()));
                    }
                }
            }
            itemService.saveBatch(dto.items);
        }
        if (dto.submit) {
            return R.ok(billService.submit(String.valueOf(bill.getBillId())));
        }
        return R.ok(true);
    }
}


